home *** CD-ROM | disk | FTP | other *** search
- /* TTY input driver */
-
- #include <stdio.h>
- #include <ctype.h>
- #include "global.h"
-
- int ttymode;
- #define TTY_COOKED 0
- #define TTY_RAW 1
-
- #define LINESIZE 256
- #define MAXHIST 25
-
- #define CTLR 18
- #define CTLX 24
- #define CTLU 21
- #define CTLV 22
- #define CTLW 23
- #define CTLZ 26
-
- struct linehist
- {
- struct linehist *next;
- struct linehist *prev;
- char linebuf[1];
- };
- #define NULLHIST (struct linehist *)0
-
- struct linehist *linehist = NULLHIST;
-
- raw()
- {
- ttymode = TTY_RAW;
- }
-
- cooked()
- {
- ttymode = TTY_COOKED;
- }
-
- static void echochar (c) /* echo character */
- unsigned char c;
-
- {
- if (c < ' ') { /* a control char? */
- putchar('^'); /* show as ^char */
- c += 64;
- }
-
- putchar(c);
- }
-
- static void bschar (c) /* backspace over character */
- unsigned char c;
-
- {
- if (c < ' ') /* a control char? */
- putchar('\b'); /* 2 backspaces needed */
-
- putchar('\b');
- }
-
- static void erachar (c) /* erase character */
- unsigned char c;
-
- {
- if (c < ' ') /* a control char? */
- putchar(' '); /* 2 spaces needed */
-
- putchar(' ');
-
- if (c < ' ') /* a control char? */
- putchar('\b'); /* 2 backspaces needed */
-
- putchar('\b');
- }
-
- /* Accept characters from the incoming tty buffer and process them
- * (if in cooked mode) or just pass them directly (if in raw mode).
- * Returns the number of characters available for use; if non-zero,
- * also stashes a pointer to the character(s) in the "buf" argument.
- */
- /*Control-R added by df for retype of lines - useful in Telnet */
- /* full editing and history added by PE1CHL */
-
- int
- ttydriv(c,buf)
- int c;
- char **buf;
- {
- static char linebuf[LINESIZE + 1];
- static char *cp = linebuf;
- static struct linehist *lh = NULLHIST;
- static char insmode = 0;
- static char quote = 0;
- static int tailchars = 0;
- register char *p,*q;
- int cnt,i,seenprint;
-
- if(c == -1) /* -1 is used to inquire buffered chars */
- return((int) (cp - linebuf) + tailchars);
-
- #ifdef DEBUG
- if(buf == (char **)NULL)
- return 0; /* paranoia check */
- #endif
-
- /* initialize buffer when 1st call for this line */
- if(cp == linebuf && !tailchars){
- memset(linebuf,0,LINESIZE);
- if((lh = linehist) != NULLHIST)
- strcpy(linebuf,lh->linebuf);
- }
-
- cnt = 0;
- switch(ttymode){
- case TTY_RAW:
- tailchars = 0; /* clear the COOKED line buffer */
- insmode = quote = 0;
- cp = linebuf;
- switch(c){
- case -20: /* HOME key */
- strcpy(linebuf,"\033[H");
- cp += 3;
- break;
- case -19: /* UP arrow */
- strcpy(linebuf,"\033[A");
- cp += 3;
- break;
- case -17: /* LEFT arrow */
- strcpy(linebuf,"\033[D");
- cp += 3;
- break;
- case -16: /* RIGHT arrow */
- strcpy(linebuf,"\033[C");
- cp += 3;
- break;
- case -14: /* DOWN arrow */
- strcpy(linebuf,"\033[B");
- cp += 3;
- break;
- default: /* a normal character */
- *cp++ = c;
- break;
- }
- cnt = (int) (cp - linebuf); /* return count of characters */
- break;
-
- case TTY_COOKED:
- /* Perform cooked-mode line editing */
- if (quote) /* prev char was quote? (^V) */
- goto storechar; /* then store anything */
-
- switch(c){
- case '\r': /* CR and LF are equivalent */
- case '\n':
- while(tailchars > 0){ /* move to end of line */
- echochar(*cp++);
- tailchars--;
- }
- *cp++ = '\r';
- *cp++ = '\n';
- *cp = '\0';
- printf("\n");
- cnt = (int) (cp - linebuf);
- cp = linebuf;
- if(cnt > 3){
- i = 0;
- for(lh = linehist; lh != NULLHIST; lh = lh->next){
- if(!strcmp(lh->linebuf,linebuf) || ++i > MAXHIST){
- if(lh->prev != NULLHIST)
- lh->prev->next = lh->next;
- else
- linehist = lh->next;
-
- if(lh->next != NULLHIST)
- lh->next->prev = lh->prev;
- free(lh);
- }
- }
- if((lh = (struct linehist *) malloc(sizeof(struct linehist) + cnt)) != NULLHIST){
- if((lh->next = linehist) != NULLHIST)
- lh->next->prev = lh;
- lh->prev = NULLHIST;
- linehist = lh;
- strcpy(lh->linebuf,linebuf);
- }
- }
- insmode = 0;
- break;
- case 0x7f: /* Delete */
- if (tailchars > 0){
- tailchars--;
- c = *cp;
- for(i = 0, p = cp, q = cp + 1; i < tailchars; i++){
- echochar(p[1]);
- *p++ = *q++;
- }
- erachar(c);
- *p = '\0';
- for(i = 0; i < tailchars; i++)
- bschar(*--p);
- } else {
- goto storechar;
- }
- break;
- case '\b': /* Backspace */
- if(cp != linebuf){
- bschar(c = *--cp);
- for(i = 0, p = cp, q = cp + 1; i < tailchars; i++){
- echochar(p[1]);
- *p++ = *q++;
- }
- erachar(c);
- *p = '\0';
- for(i = 0; i < tailchars; i++)
- bschar(*--p);
- }
- break;
- case CTLR: /* print line buffer */
- echochar(c); /* ^R */
- putchar('\n');
- p = linebuf;
- cp += tailchars;
- while(p < cp)
- echochar(*p++);
- tailchars = 0;
- break;
- case CTLU: /* Line kill */
- case CTLX: /* Also a line kill */
- while(tailchars > 0){
- echochar(*cp++);
- tailchars--;
- }
- while(cp != linebuf){
- bschar(*--cp);
- erachar(*cp);
- }
- break;
- case CTLV: /* Literal Next */
- quote = 1;
- c = '^'; /* insert a ^ temporarily */
- goto storechar;
- case CTLW: /* Word Erase */
- seenprint = 0; /* Haven't seen a printable char yet */
- while(cp != linebuf){
- if (!isspace(cp[-1])) /* Next is a printable char? */
- seenprint = 1;
- else
- if (seenprint) /* Next is a space, quit when we */
- break; /* already erased characters */
- bschar(c = *--cp);
- for(i = 0, p = cp, q = cp + 1; i < tailchars; i++){
- echochar(p[1]);
- *p++ = *q++;
- }
- erachar(c);
- *p = '\0';
- for(i = 0; i < tailchars; i++)
- bschar(*--p);
- }
- break;
- case -14: /* down-arrow = next in history */
- if(lh != NULLHIST && lh->prev != NULLHIST){
- lh = lh->prev;
- while(tailchars > 0){ /* move to end of line */
- echochar(*cp++);
- tailchars--;
- }
- while(cp != linebuf){ /* erase it */
- bschar(*--cp);
- erachar(*cp);
- }
- memset(linebuf,0,LINESIZE);
- strcpy(linebuf,lh->linebuf);
- for(cp = linebuf; *cp && *cp != '\r'; cp++){
- echochar(*cp);
- }
- }
- break;
- case -12: /* insert */
- insmode ^= 1; /* toggle insert mode */
- break;
- case -16: /* right-arrow = buffer character */
- insmode = 0; /* insert mode screws things up */
- if(*cp && *cp != '\r'){
- c = *cp;
- goto storechar;
- }
- break;
- case -17: /* left-arrow = nondestruct backspace */
- if(cp != linebuf){
- tailchars++;
- bschar(*--cp);
- }
- break;
- case -19: /* up-arrow = prev in history */
- if(lh != NULLHIST){
- while(tailchars > 0){ /* move to end of line */
- echochar(*cp++);
- tailchars--;
- }
- while(cp != linebuf){ /* erase it */
- bschar(*--cp);
- erachar(*cp);
- }
- memset(linebuf,0,LINESIZE);
- strcpy(linebuf,lh->linebuf);
- if (lh->next != NULLHIST)
- lh = lh->next;
- for(cp = linebuf; *cp && *cp != '\r'; cp++)
- echochar(*cp);
- }
- break;
- case -20: /* HOME = begin of line */
- while(cp != linebuf){
- tailchars++;
- bschar(*--cp);
- }
- break;
- case -15: /* END = end of line */
- while(tailchars > 0){
- echochar(*cp++);
- tailchars--;
- }
- break;
- default: /* Ordinary character */
- if(c < 0) /* a non-decoded special character? */
- break; /* ignore it */
-
- storechar:
- echochar(c);
-
- if(insmode && quote < 2){
- for(i = 0, p = cp; i < tailchars; i++)
- echochar(*p++);
- for(i = 0, q = p + 1; i < tailchars; i++)
- bschar(*--q = *--p);
- } else {
- if(tailchars > 0){
- tailchars--;
- if(c < ' ' || *cp < ' '){
- for(i = 0, p = cp + 1; i < tailchars; i++)
- echochar(*p++);
- erachar(' ');
- for(i = 0; i < tailchars; i++)
- bschar(*--p);
- }
- }
- }
-
- if(quote)
- if(quote == 1){ /* immediately after ^V */
- quote = 2;
- bschar(c); /* backspace to the '^' */
- tailchars++;
- break;
- } else
- quote = 0;
-
- *cp++ = c;
-
- if((cp + tailchars) >= &linebuf[LINESIZE]){
- while(tailchars > 0){ /* move to end of line */
- echochar(*cp++);
- tailchars--;
- }
- cnt = (int) (cp - linebuf);
- cp = linebuf;
- }
- break;
- }
- }
- if(cnt != 0)
- *buf = linebuf;
- else
- *buf = NULLCHAR;
- fflush(stdout);
- return cnt;
- }
-